package com.demo.entity.response;

import com.alibaba.fastjson.JSONObject;
import com.demo.common.enums.ResultEnum;
import com.demo.common.exception.BusinessException;
import com.demo.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletResponse;

/**
 * 如果引入了 swagger 或 knife4j 的文档生成组件，这里需要仅扫描自己项目的包，否则文档无法正常生成
 * 实现 ResponseBodyAdvice 接口，其实是对加了 @RestController 注解的处理器将要返回的值进行增强处理。
 * 其实也就是采用了AOP的思想，对返回值进行一次修改
 */
//@RestControllerAdvice(basePackages = "com.demo")
@ControllerAdvice
@Slf4j
@Order(2)
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 如果不需要进行封装的，可以添加一些校验手段，比如添加标记排除的注解
//        System.out.println("returnType = " + returnType.toString());
//        System.out.println("converterType = " + converterType.toString());
        return true;
    }

    /**
     * @param body 原 controller 要返回的内容
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//        System.out.println("request.getURI() = " + request.getURI());
//        System.out.println("path = " + request.getURI().getPath());
//        if (request.getURI().getPath().equals("/error")) {
//            Map errorMap = (Map) body;
            //{timestamp=Tue Apr 25 16:13:08 CST 2023, status=404, error=Not Found, message=No message available, path=/favicon.ico}
//            System.out.println(errorMap.toString());
//            return Result.build(false, (Integer) errorMap.get("status"), String.valueOf(errorMap.get("error")),null);
//            return Result.error((Integer) errorMap.get("status"), errorMap.get("error").toString());
//        }
        // 提供一定的灵活度，如果body已经被包装了，就不进行包装
        if (body instanceof Result) {
            return body;
        }
        if (body instanceof String) {
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return JSONObject.toJSONString(Result.build(true, 200, "", body));
        }

        return Result.success(body);
    }

    /**
     * 默认异常
     * 捕获所有异常
     * 异常会优先进入最小异常，如果都不匹配，则走这个默认异常
     * 例如 ArithmeticException，会优先进入 handleArithmeticException，而不是进入 handleException
     */
    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public Result<Object> handleException(Throwable t, HttpServletResponse response) throws Throwable {
        log.error("默认异常：{}", t.getMessage(), t);//可以通过这样的方式打印异常到错误文件
        return Result.error(t.getMessage());
    }

    /**
     * 算数异常
     */
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public Result<Object> handleArithmeticException(ArithmeticException ae) {
        ae.printStackTrace();
//        log.error(ae.toString());//此处不用打印错误信息到日志文件，如果要打印日志信息，就在业务中打印
        //可以通过这样的方式打印异常到错误文件
        log.error("算数异常: {}", ae.getMessage(), ae);
        return Result.error(ae.getMessage());
    }

    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public Result<Object> businessExceptionHandler(BusinessException e) {
        log.error("business error : {}", e.getMessage(), e);
        if (e.getCode() == -1) {
            return Result.error(ResultEnum.INTERNAL_SERVER_ERROR.getCode(), ResultEnum.INTERNAL_SERVER_ERROR.getMessage());
        }
        return Result.error(e.getCode(), e.getMessage());
    }
}
